#pragma once

#define HZ 100

#include <linux/tasks.h>
#include <asm/system.h>

#define USER_BASE      0x40000000
#define USER_SIZE      0xC0000000

#define IO_BITMAP_SIZE 32

#include <linux/head.h>
#include <linux/wait.h>
#include <linux/mm.h>
#include <linux/time.h>

#define TASK_RUNNING         0
#define TASK_INTERRUPTIBLE   1
#define TASK_UNINTERRUPTIBLE 2
#define TASK_ZOMBIE          3
#define TASK_STOPPED         4
#define TASK_SWAPPING        5

#ifndef NULL
#define NULL ((void *)0)
#endif

#ifdef __KERNEL__

extern void sched_init(void);
extern void trap_init(void);

void schedule(void);

#endif

struct i387_hard_struct {
    long cwd;
    long swd;
    long twd;
    long fip;
    long fcs;
    long foo;
    long fos;
    long st_space[20]; /* 8*10 bytes for each FP-reg = 80 bytes */
};

struct tss_struct {
    unsigned short back_link, __blh;
    unsigned long esp0;
    unsigned short ss0, __ss0h;
    unsigned long esp1;
    unsigned short ss1, __ss1h;
    unsigned long esp2;
    unsigned short ss2, __ss2h;
    unsigned long cr3;
    unsigned long eip;
    unsigned long eflags;
    unsigned long eax, ecx, edx, ebx;
    unsigned long esp;
    unsigned long ebp;
    unsigned long esi;
    unsigned long edi;
    unsigned short es, __esh;
    unsigned short cs, __csh;
    unsigned short ss, __ssh;
    unsigned short ds, __dsh;
    unsigned short fs, __fsh;
    unsigned short gs, __gsh;
    unsigned short ldt, __ldth;
    unsigned short trace, bitmap;
    unsigned long off, tr;
    unsigned long cr2, trap_no, error_code;
    struct i387_hard_struct i387;
};

struct task_struct {
    volatile long state;
    long counter;
    long priority;
    struct task_struct *next_task, *prev_task;
    unsigned long kernel_stack_page;
    int exit_code;
    unsigned long start_code;
    int pid, pgrp, session;
    struct task_struct *p_opptr, *p_pptr, *p_cptr, *p_ysptr, *p_osptr;
    long utime, stime;
    struct desc_struct ldt[3];
    struct tss_struct tss;
};

#define INIT_TASK \
    { \
        0, 15, 15, &init_task, &init_task, (unsigned long)&init_kernel_stack, 0, 0, \
            /*pid*/ 0, 0, 0, &init_task, &init_task, NULL, NULL, NULL, 0, 0, \
            /*ldt*/ {{0, 0}, {0xffff, 0xcffa00}, {0xffff, 0xcff200}}, /*tss*/ { \
            0, 0, \
                sizeof(init_kernel_stack) + (long)&init_kernel_stack, KERNEL_DS, 0, \
                0, 0, 0, 0, 0, 0, \
                __pa((long)&swapper_pg_dir), \
                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
                USER_DS, 0, USER_DS, 0, USER_DS, 0, USER_DS, 0, USER_DS, 0, USER_DS, 0, \
                _LDT(0), 0, \
                0, 0x8000, \
                0, _TSS(0), 0, 0, 0, /* 387 state */ {} \
        } \
    }

extern struct task_struct init_task;
extern struct task_struct *task[NR_TASKS];
extern struct task_struct *current;
extern int need_resched;

#define FIRST_TSS_ENTRY 8
#define FIRST_LDT_ENTRY (FIRST_TSS_ENTRY + 1)
#define _TSS(n)         ((((unsigned long)n) << 4) + (FIRST_TSS_ENTRY << 3))
#define _LDT(n)         ((((unsigned long)n) << 4) + (FIRST_LDT_ENTRY << 3))
#define load_TR(n)      __asm__("ltr %%ax" \
                           : /* no output */ \
                           : "a"(_TSS(n)))
#define load_ldt(n) __asm__("lldt %%ax" \
                            : /* no output */ \
                            : "a"(_LDT(n)))

#define switch_to(tsk) \
    __asm__("cmpl %%ecx, current\n\t" \
            "je 1f\n\t" \
            "cli\n\t" \
            "xchgl %%ecx, current\n\t" \
            "ljmp *%0\n\t" \
            "sti\n\t" \
            "1:" \
            : /* no output */ \
            : "m"(*(((char *)&tsk->tss.tr) - 4)), \
              "c"(tsk))

#define _set_base(addr, base) \
    __asm__("movw %%dx, %0\n\t" \
            "rorl $16, %%edx\n\t" \
            "movb %%dl, %1\n\t" \
            "movb %%dh, %2" \
            : /* no output */ \
            : "m"(*((addr) + 2)), \
              "m"(*((addr) + 4)), \
              "m"(*((addr) + 7)), \
              "d"(base))

#define _set_limit(addr, limit) \
    __asm__("movw %%dx, %0\n\t" \
            "rorl $16, %%edx\n\t" \
            "movb %1, %%dh\n\t" \
            "andb $0xf0, %%dh\n\t" \
            "orb %%dh, %%dl\n\t" \
            "movb %%dl, %1" \
            : /* no output */ \
            : "m"(*(addr)), \
              "m"(*((addr) + 6)), \
              "d"(limit))

#define set_base(ldt, base)   _set_base(((char *)&(ldt)), base)
#define set_limit(ldt, limit) _set_limit(((char *)&(ldt)), (limit - 1) >> 12)

static inline void add_wait_queue(struct wait_queue **p, struct wait_queue *wait) {
    unsigned long flags;

    save_flags(flags);
    cli();
    if (!*p) {
        wait->next = wait;
        *p = wait;
    } else {
        wait->next = (*p)->next;
        (*p)->next = wait;
    }
    restore_flags(flags);
}

static inline void remove_wait_queue(struct wait_queue **p, struct wait_queue *wait) {
    unsigned long flags;
    struct wait_queue *tmp;

    save_flags(flags);
    cli();
    if ((*p == wait) && ((*p = wait->next) == wait)) {
        *p = NULL;
    } else {
        tmp = wait;
        while (tmp->next != wait) {
            tmp = tmp->next;
        }
        tmp->next = wait->next;
    }
    wait->next = NULL;
    restore_flags(flags);
}

#define REMOVE_LINKS(p) \
    do { \
        unsigned long flags; \
        save_flags(flags); \
        cli(); \
        (p)->next_task->prev_task = (p)->prev_task; \
        (p)->prev_task->next_task = (p)->next_task; \
        restore_flags(flags); \
        if ((p)->p_osptr) \
            (p)->p_osptr->p_ysptr = (p)->p_ysptr; \
        if ((p)->p_ysptr) \
            (p)->p_ysptr->p_osptr = (p)->p_osptr; \
        else \
            (p)->p_pptr->p_cptr = (p)->p_osptr; \
    } while (0)

#define SET_LINKS(p) \
    do { \
        unsigned long flags; \
        save_flags(flags); \
        cli(); \
        (p)->next_task = &init_task; \
        (p)->prev_task = init_task.prev_task; \
        init_task.prev_task->next_task = (p); \
        init_task.prev_task = (p); \
        restore_flags(flags); \
        (p)->p_ysptr = NULL; \
        if (((p)->p_osptr = (p)->p_pptr->p_cptr) != NULL) \
            (p)->p_osptr->p_ysptr = p; \
        (p)->p_pptr->p_cptr = p; \
    } while (0)

#define for_each_task(p) \
    for (p = &init_task; (p = p->next_task) != &init_task;)
